//
//  CentralCache.cpp
//  高并发内存池
//
//  Created by 卜绎皓 on 2023/2/2.
//

#include "CentralCache.hpp"
#include "PageCache.hpp"

CentralCache CentralCache::_sInst;

Span* CentralCache::GetOneSpan(SpanList& list, size_t size)
{
    //1、先在spanList中寻找非空的span
    Span* it = list.Begin();
    while(it != list.End())
    {
        if(it->_freeList != nullptr)
        {
            return it;
        }
        else
        {
            it = it->_next;
        }
    }
    
    list._mtx.unlock(); //解桶锁
    
    PageCache::GetInstance()->_pageMtx.lock(); //加大锁
    
    //2、spanList中没有非空的span，只能向page cache申请
    Span* span = PageCache::GetInstance()->NewSpan(SizeClass::NumMovePage(size));
    
    PageCache::GetInstance()->_pageMtx.unlock(); //解大锁
    
    //计算span的大块内存的起始地址和大块内存的大小（字节数）
    char* start = (char*)(span->_pageId << PAGE_SHIFT);
    size_t bytes = span->_n << PAGE_SHIFT;
    
    //把大块内存切成size大小的对象链接起来
    char* end = start + bytes;
    
    //先切一块下来去做尾，方便尾插
    span->_freeList = start;
    start += size;
    void* tail = span->_freeList;
    
    //尾插
    while (start < end)
    {
        NextObj(tail) = start;
        tail = NextObj(tail);
        start += size;
    }
    NextObj(tail) = nullptr; //尾的指向置空
        
    list._mtx.lock(); //加桶锁
    
    //将切好的span头插到spanList
    list.PushFront(span);

    return span;
}

//从central cache获取一定数量的对象给thread cache
size_t CentralCache::FetchRangeObj(void*& start, void*& end, size_t batchNum, size_t size)
{
    size_t index = SizeClass::Index(size);
    _spanLists[index]._mtx.lock();//加锁
    
    //在对应哈希桶中获取一个非空的span
    Span* span = GetOneSpan(_spanLists[index], size);
    assert(span); //span不为空
    assert(span->_freeList); //span当中的自由链表也不为空
    
    //从span中获取n个对象
    //如果不够n个，有多少拿多少
    start = span->_freeList;
    end = start;
    size_t i = 0;
    size_t actualNum = 1;
    while ( i < batchNum - 1 && NextObj(end) != nullptr)
    {
        end = NextObj(end);
        ++actualNum;
        ++i;
    }
    span->_freeList = NextObj(end); //取完后剩下的对象继续放到自由链表
    NextObj(end) = nullptr; //取出的一段链表的表尾置空
//    span->_useCount += actualNum; //更新被分配给thread cache的计数

    _spanLists[index]._mtx.unlock(); //解锁
    
    return actualNum;
    
}
